package com.bee.app.tool;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * 类扫描工具
 * 
 * @author liuzy
 * @version 2015年7月2日
 */
public class ClassSearcher {

	/**
	 * 搜索路径中所有clazz的子类
	 * 
	 * @param clazz
	 * @param path
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> List<Class<? extends T>> search(Class clazz, String path) {
		path = path.replace('.', '/');
		path = Thread.currentThread().getContextClassLoader().getResource(path).getPath();
		List<String> fileList = findFiles(path);
		return extraction(clazz, fileList);
	}

	/**
	 * 递归查找class文件
	 * 
	 * @param baseDirName
	 * @param targetFileName
	 */
	private static List<String> findFiles(String baseDirName) {
		/**
		 * 算法简述： 从某个给定的需查找的文件夹出发，搜索该文件夹的所有子文件夹及文件，
		 * 若为文件，则进行匹配，匹配成功则加入结果集，若为子文件夹，则进队列。 队列不空，重复上述操作，队列为空，程序结束，返回结果。
		 */
		List<String> classFiles = new ArrayList<String>();
		File baseDir = new File(baseDirName);
		if (baseDir.exists() && baseDir.isDirectory()) {
			String[] filelist = baseDir.list();
			for (int i = 0; i < filelist.length; i++) {
				File readfile = new File(baseDirName + File.separator + filelist[i]);
				if (readfile.isDirectory()) {
					classFiles.addAll(findFiles(baseDirName + File.separator + filelist[i]));
				} else {
					String tem = readfile.getAbsoluteFile().toString().replaceAll("\\\\", "/");
					if (tem.indexOf(".class") > 0) {
						String classname = tem.substring(tem.indexOf("/classes") + "/classes".length() + 1, tem.indexOf(".class"));
						classFiles.add(classname.replaceAll("/", "."));
					}
				}
			}
		}
		return classFiles;
	}

	/**
	 * 抽取list中clazz的子类
	 * 
	 * @param clazz
	 * @param fileList
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static <T> List<Class<? extends T>> extraction(Class<T> clazz, List<String> fileList) {
		List<Class<? extends T>> classList = new ArrayList<Class<? extends T>>();
		for (String classFile : fileList) {
			Class<?> fileClass = null;
			try {
				fileClass = Thread.currentThread().getContextClassLoader().loadClass(classFile);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
			if (clazz.isAssignableFrom(fileClass) && clazz != fileClass) {
				classList.add((Class<? extends T>) fileClass);
			}
		}

		return classList;
	}
}
